﻿using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using System.Xml;

namespace integrationServer
{
    class HttpProcessor
    {
        // Provides client connections for TCP network services
        private TcpClient s = null;
        // Network stream => Read and write
        private NetworkStream netStream;
        // Read a stream
        private StreamReader sr = null;

        // When the user POST a request I save the value in this following attributes
        // users
        private String first = "";
        private String login = "";
        private String passwd = "";
        // Boards
        private String player = "";
        private String owner = "";
        private String invite = "";
        private String size = "";
        private String ownerWhite = "";
        // Invites
        private String boardUrl = "";
        // Request 
        private String gamePermission = "";

        // Header of the HTTP request
        private String method = "";
        private String url = "";
        private String protocol = "";
        private String trame = "";

        // Message return to the user
        private String message = "";

        // The body of the client srequest
        private string[] body = null;

        // Ludo function
        private Form1 traitementServer = new Form1();

        // The different response to the client
        private String response200 = "HTTP/1.0 200 OK";
        private String response201 = "HTTP/1.0 201 Created & Location";
        private String response204 = "HTTP/1.0 204 Created & No Content";
        private String response401 = "HTTP/1.0 401 Unauth";
        private String response403 = "HTTP/1.0 403 Forbid. (rsc exist)";
        private String response404 = "HTTP/1.0 404 Not Found";
        private String response415 = "HTTP/1.0 415 Unsupported Media Type";

        public HttpProcessor(TcpClient s)
        {
            this.s = s;
        }

        // Main function
        public void process()
        {
            // Get the socket stream 
            this.netStream = this.s.GetStream();

            // To read the network stream
            this.sr = new StreamReader(netStream);

            // Read the query 
            this.readRequest();
        }


        // Read the client request : Get the header and manage
        public void readRequest()
        {
            // The header
            String request = this.sr.ReadLine();

            String[] tokens = request.Split(new char[] { ' ' });

            // Set value for my request attribute
            this.method = tokens[0];
            this.url = tokens[1];
            this.protocol = tokens[2];

            // Read the all stream
            while (this.sr.Peek() >= 0)
            {
                this.trame += (char)this.sr.Read();
            }

            if (this.method == "POST")
            {
                // Get the body after the \n\n
                this.body = this.trame.Split(new String[] { "\r\n\r\n" }, StringSplitOptions.None);

                // Call the method who control the POST request
                this.managePost();
            }
            // It is a GET request 
            else if (this.method == "GET")
            {
                this.manageGet();
            }
            // The user want to DELETE
            else if (this.method == "DELETE")
            {
                this.manageDelete();
            }
        }

        // ================================ MANAGE THE HTTP METHOD REQUEST =====================================
        // Get request
        public void manageGet()
        {
            // Split the URL
            String[] retrievedRequest = this.url.Split('/');

            // Get the simple url of the server
            if (retrievedRequest[1] == "\\" || retrievedRequest[1] == "")
            {
                this.message = this.response200 + "\r\n\r\n" + "OK";
                this.response();
            }

            // URL = Bad URL
            if (retrievedRequest[1] != "/" && retrievedRequest[1] != "users" && retrievedRequest[1] != "boards")
            {
                this.message = this.response404 + "\r\n\r\n" + "Read the specification !!";
                this.response();
            }

            // URL = /users/
            if (retrievedRequest[1] == "users" && retrievedRequest[2] == "")
            {
                string retour = traitementServer.ListerUser();

                if (retour.Equals(""))
                {
                    this.message = this.response404 + "\r\n\r\n" + "Page not found";
                    this.response();
                }
                else
                {
                    this.message = this.response200 + "\r\n\r\n" + retour;
                    this.response();
                }
            }
            // URL = /boards/
            else if (retrievedRequest[1] == "boards" && retrievedRequest[2] == "")
            {
                string retour = traitementServer.ListerUser();

                if (retour.Equals(null))
                {
                    this.message = this.response404 + "\r\n\r\n" + "Page not found";
                    this.response();
                }
                else
                {
                    this.message = this.response200 + "\r\n\r\n" + retour;
                    this.response();
                }
            }
            // URL = /boards/[board]/moves
            else if (retrievedRequest[1] == "boards" && retrievedRequest[2] != "" && retrievedRequest[3] != "")
            {
                string retour = traitementServer.lister1Board(retrievedRequest[2]);

                if (retour.Equals(null))
                {
                    this.message = this.response404 + "\r\n\r\n" + "Page not found";
                    this.response();
                }
                else
                {
                    this.message = this.response200 + "\r\n\r\n" + retour;
                    this.response();
                }
            }
            // URL = /boards/board/
            else
            {
                string retour = traitementServer.ListerBoard();

                if (retour.Equals(null))
                {
                    this.message += this.response404 + "\r\n\r\n" + "Page not found";
                    this.response();
                }
                else
                {
                    this.message += this.response200 + "\r\n\r\n" + retour;
                    this.response();
                }
            }
        }

        // Manage POST request 
        public void managePost()
        {
            // Split the URL
            String[] retrievedRequest = this.url.Split('/');

            // URL = /users/
            if (retrievedRequest[1] == "users" && retrievedRequest[2] == "")
            {
                // Copy the body and a string
                string tmpBody = this.body[1];

                // Create an XmlReader
                using (XmlReader reader = XmlReader.Create(new StringReader(tmpBody)))
                {
                    // Read the attribute fullname
                    reader.ReadToFollowing("fullname");
                    reader.MoveToFirstAttribute();
                    // Read the value
                    this.first = reader.Value;

                    // Next ! read the following attribute
                    reader.ReadToFollowing("screenname");
                    reader.MoveToNextAttribute();
                    this.login = reader.Value;

                    reader.ReadToFollowing("password");
                    reader.MoveToNextAttribute();
                    this.passwd = reader.Value;
                }

                // Call the method of ludo
                int retour = traitementServer.CreerUser(this.first, this.login, this.passwd,this.url);

                if (retour != 1)
                {
                    this.message = this.response403 + "\r\n\r\n" + "The gamer already exist";
                    this.response();
                }
                else
                {
                    Console.WriteLine("gamer ok");
                    this.message = this.response201 + "\r\n\r\n" + "Gamer create OK";
                    this.response();
                }
            }
            // URL = /boards/
            else if (retrievedRequest[1] == "boards" && retrievedRequest[2] == "")
            {
                // Copy the body and a string
                string tmpBody = this.body[1];

                // Create an XmlReader
                using (XmlReader reader = XmlReader.Create(new StringReader(tmpBody)))
                {
                    // Read the attribute fullname
                    reader.ReadToFollowing("name");
                    reader.MoveToFirstAttribute();
                    // Read the value
                    this.player = reader.Value;

                    // Next ! read the following attribute
                    reader.ReadToFollowing("owner");
                    reader.MoveToNextAttribute();
                    this.owner = reader.Value;

                    reader.ReadToFollowing("invitee");
                    reader.MoveToNextAttribute();
                    this.invite = reader.Value;

                    reader.ReadToFollowing("size");
                    reader.MoveToNextAttribute();
                    this.size = reader.Value;

                    reader.ReadToFollowing("owner_white");
                    reader.MoveToNextAttribute();
                    this.ownerWhite = reader.Value;
                }

                int retour = traitementServer.CreerBoard(this.owner, this.invite, this.player, this.size, System.Convert.ToBoolean(this.ownerWhite));

                if (retour < 1)
                {
                    this.message = this.response401 + "\r\n\r\n" + "Unauthorized";
                    this.response();
                }
                else if (retour == 1) 
                {
                    this.message = this.response201 + "\r\n\r\n" + "Creation OK";
                    this.response();
                }
                else if(retour > 1) 
                {
                    this.message = this.response415 + "\r\n\r\n" + "Unsupported Media Type";
                    this.response();
                }
                 
            }
            // URL = /users/[user]/invites/
            else if (retrievedRequest[1] == "user" && retrievedRequest[2] != "" && retrievedRequest[3] != "")
            {
                // Copy the body and a string
                string tmpBody = this.body[1];

                // Create an XmlReader
                using (XmlReader reader = XmlReader.Create(new StringReader(tmpBody)))
                {
                    // Read the attribute fullname
                    reader.ReadToFollowing("board");
                    reader.MoveToFirstAttribute();
                    // Read the value 
                    this.boardUrl = reader.Value;
                }

                int retour = traitementServer.InviteUserJoinBoard(retrievedRequest[2], retrievedRequest[3], this.boardUrl);

                if (retour == 2)
                {
                    this.message = this.response401 + "\r\n\r\n" + "Unauthorized";
                    this.response();
                }
                else if (retour == 1)
                {
                    this.message = this.response204 + "\r\n\r\n" + "Post user/invites OK";
                    this.response();
                }
                else if(retour < 1) 
                {
                    this.message = this.response404 + "\r\n\r\n" + "Not found";
                    this.response();
                }
                else if (retour == 3)
                {
                    this.message = this.response415 + "\r\n\r\n" + "Unsupported Media Type";
                    this.response();
                }

            }
            // URL = /boards/[board]/request
            else if (retrievedRequest[1] == "boards" && retrievedRequest[2] != "" && retrievedRequest[3] != "")
            {
                // Copy the body and a string
                string tmpBody = this.body[1];

                // Create an XmlReader
                using (XmlReader reader = XmlReader.Create(new StringReader(tmpBody)))
                {
                    // Read the attribute fullname
                    reader.ReadToFollowing("sender");
                    reader.MoveToFirstAttribute();
                    // Read the value 
                    this.gamePermission = reader.Value;
                }

                int retour = traitementServer.PermissionToJoin(this.gamePermission, retrievedRequest[1]);

                if (retour < 1)
                {
                    this.message = this.response204 + "\r\n\r\n" + "Page not found";
                    this.response();
                }
                else if (retour == 1)
                {
                    this.message = this.response204 + "\r\n\r\n" + "boards/request OK";
                    this.response();
                }
                else if(retour == 4) 
                {
                    this.message = this.response404 + "\r\n\r\n" + "Not found";
                    this.response();
                }
                    else if(retour == 2) 
                    {
                        this.message = this.response415 + "\r\n\r\n" + "Unsupported Media Type";
                        this.response();
                    }
            }

            // URL = /boards/[board]/moves
            /* Not implemented 
            else if (retrievedRequest[1] == "boards" && retrievedRequest[2] != "" && retrievedRequest[2] == "moves")
            {
                      
            }
            */
        }

        // Delete
        public void manageDelete()
        {
            // Split the URL
            String[] retrievedRequest = this.url.Split('/');

            // URL = /boards/[board]
            if (retrievedRequest[1] == "board" && retrievedRequest[2] != "")
            {
                int retour = traitementServer.EffacerBoard(retrievedRequest[1]);

                if (retour != 1)
                {
                    this.message = this.response404 + "\r\n\r\n" + "Not found";
                    this.response();
                }
                else
                {
                    this.message = this.response200 + "\r\n\r\n" + "Delete Ok";
                    this.response();
                }
            }
            else
            {
                this.message = this.response404 + "\r\n\r\n" + "Page not found";
                this.response();
            }
        }

        // Response to the client
        public void response()
        {
            if (this.netStream.CanWrite)
            {
                // Encoding the message in the format UTF8
                Byte[] sendBytes = Encoding.UTF8.GetBytes(this.message);//);

                // Use the Stream write method
                this.netStream.Write(sendBytes, 0, sendBytes.Length);

                // Flush the stream 
                this.netStream.Flush();

                // Close the socket
                this.s.Close();

                // Close the stream
                this.netStream.Close();
            }
            else
            {
                // Flush the stream 
                this.netStream.Flush();

                // Close the socket
                this.s.Close();

                // Closing the tcpClient instance does not close the network stream.
                this.netStream.Close();
            }
        }
    }
}
